/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package fr.inria.openmath.omapi.implementation;

/**
 *
 * @author Mr.Sam
 */

import java.util.Enumeration;
import fr.inria.openmath.omapi.*;
import fr.inria.openmath.omapi.Node.*;
import java.io.*;


public class TreePrinterImpl implements TreePrinter {

    Printer printer;

    public TreePrinterImpl () {
        this.printer = new XMLPrinter(new DataOutputStream(System.out));
    }

    public TreePrinterImpl (Printer printer) {
        this.printer = printer;
    }

    public void setPrinter(Printer output) throws OMException {
        this.printer = printer;
    }

    public Printer getPrinter() {
        return this.printer;
    }


    private void printBeginTag(Node root) throws OMException {
        try {
            switch (root.getNodeType()) {
                case Node.OM_APP: 
                    printer.putBeginApplication();
                    break;
                case Node.OM_BIND: 
                    printer.putBeginBind();
                    break;
                case Node.OM_ERROR: 
                    printer.putBeginError();
                    break;
                case Node.OM_OBJECT: 
                    printer.putBeginObject();
                    break;
                default: 
                    throw new OMException("unknown tag type",
                            OMException.OME_INTERNAL);
            }
        } catch (IOException e) {
            throw new OMException("I/O error", e);
        }
    }

    private void printEndTag(Node root) throws OMException {
        try {
            switch (root.getNodeType()) {
                case Node.OM_APP: 
                    printer.putEndApplication();
                    break;
                case Node.OM_BIND: 
                    printer.putEndBind();
                    break;
                case Node.OM_ERROR: 
                    printer.putEndError();
                    break;
                case Node.OM_OBJECT: 
                    printer.putEndObject();
                    break;
                default: 
                    throw new OMException("unknown tag type",
                            OMException.OME_INTERNAL);
            }
        } catch (IOException e) {
            throw new OMException("I/O error", e);
        }

    }


    private void printLeaf(Node root) throws OMException {
        try {
            switch (root.getNodeType()) {

                case Node.OM_INT32: 
                    printer.putInteger(((IntNode) root).getValue());
                    break;
                case Node.OM_BIGINT: 
                    printer.putInteger(((BigIntegerNode) root).getValue());
                    break;
                case Node.OM_VAR: 
                    printer.putVariable(((VariableNode) root).getValue());
                    break;
                case Node.OM_FLOAT64: 
                    printer.putFloat(((FloatNode) root).getValue(),
                            ((FloatNode) root).getPreferedBase());
                    break;
                case Node.OM_BYTEARRAY: 
                    printer.putByteArray(((ByteArrayNode) root).getValue());
                    break;
                case Node.OM_STRING: 
                    printer.putString(((StringNode) root).getValue());
                    break;
                case Node.OM_SYMBOL: 
                    printer.putSymbol(((SymbolNode) root).getValue());
                    break;
                default: 
                    throw new OMException("unknown leaf type",
                            OMException.OME_INTERNAL);
            }
        } catch (IOException e) {
            throw new OMException("I/O error", e);
        }

    }

    public void printTree(Node root) throws OMException {
        NodeList nodeList;
        int i, nbNodes, nbAttributes;
        String comments;

        try {
            if (root.getPInstructions() != null) {
                for (Enumeration e = root.getPInstructions().elements();
                        e.hasMoreElements();) {
                    printer.putPInstruction((String) e.nextElement());
                }

            }

            comments = root.getComments();
            if (comments != null)
                printer.putComment(comments);

            nbAttributes = root.getAttributeNumber();
            if (nbAttributes > 0) {
                printer.putBeginAttribution();
                printer.putBeginAttributePairs();
                for (i = 0; i < nbAttributes; i++) {

                    printer.putSymbol(root.getNthAttributeName(i));
                    printTree(root.getNthAttributeValue(i));
                }
                printer.putEndAttributePairs();
            }

            if (root.hasChildNodes()) {
                printBeginTag(root);
                nodeList = root.getChildNodes();
                nbNodes = nodeList.getLength();
                if (root.getNodeType() == Node.OM_BIND) {
                    printTree(nodeList.item(0));
                    printer.putBeginBVars();
                    for (i = 2; i < nbNodes; i++) {
                        printTree(nodeList.item(i));
                    }
                    printer.putEndBVars();
                    printTree(nodeList.item(1));
                } else {
                    for (i = 0; i < nbNodes; i++) {
                        printTree(nodeList.item(i));
                    }
                }
                printEndTag(root);
            } else {
                printLeaf(root);
            }
            if (nbAttributes > 0) {
                printer.putEndAttribution();
            }
        } catch (IOException e) {
            throw new OMException("I/O error", e);
        }
    }

    public void endPrint() throws OMException {
        try {
            printer.endPrint();
        } catch (IOException e) {
            throw new OMException("I/O error", e);
        }
    }

}
